# -*- coding: utf-8 -*-

import transmissionrpc
from enum import Enum
import re
import os
import datetime
from decimal import Decimal
from lib.list_search import Operators, Clause, ListSearch

TorrentStatus = Enum('All', 'Stopped', 'Downloading', 'Seeding')

class ManageTorrents(object):
    def __init__(self, host, port):
        self._host = host
        self._port = port
        self._transmission = transmissionrpc.Client(self._host, port=self._port)
    
    def getConfig(self, conf):
        transSession = self._transmission.get_session()
        return eval("transSession.%s" % (conf))
    
    def search(self, clauses=list()):
        lstSearch = ListSearch()

        #################   
        result = list()
        torrents = self._transmission.info()
        for tor in torrents:
            result.append(torrents[tor])
        #################

        result = lstSearch(clauses, result)
        
        #result = list()
        #for tor in tmpList:
        #    obj = tmpList[tor]
        #    obj.size = self._calculate_size(tor, 'size')
        #    obj.downloaded_size = self._calculate_size(tor, 'completed')
        #    result.append(obj)
        
        return result
    
    def list(self, status=None):
        if (status == None or len(status.strip()) == 0):
            status = 'All' #if no status given, by default use All
        
        status = str(status).capitalize()
        if (not status in TorrentStatus):
            raise AttributeError("invalid status '%s'" % (status))
        
        result = list()
        tmpList = self._transmission.list() #by default use all torrents
        for tor in tmpList:
            obj = tmpList[tor]
            obj.size = self._calculate_size(tor, 'size')
            obj.downloaded_size = self._calculate_size(tor, 'completed')
            result.append(obj)
        
        #if status not equal to All, search them
        if (status != str(TorrentStatus.All)):
            lstSearch = ListSearch()
            result = lstSearch([Clause('status', Operators.Equal(re.I), value=status)], result)
            
        return result
    
    def add(self, sources):
        #convert to a list
        if (not isinstance(sources, list)):
            sources = [sources]
        
        #if the source, starts with http:// -> url
        #if doesn't starts with http:// -> file
        added = list()
        for source in sources:
            added.append(self._transmission.add_url(source))
        
        #if only has one element, return that
        if (len(added) == 1):
            added = added[0]
            
        return added
    
    def remove(self, torsId=None, deleteData=False):
        self._transmission.remove(torsId, deleteData)

    def stop(self, torsId=None):
        self._transmission.stop(torsId)
    
    def start(self, torsId=None):
        self._transmission.start(torsId)
    
    def _calculate_size(self, torId, column):
        size = 0
        files = self._transmission.get_files(torId)
        if (files):
            files = files[torId]
            for fl in files:
                size += files[fl][column]
        
        return size
    
    #get the objects torrent that correspond to the given id's
    # def _get_torrents(self, torsId, tp):
        # torrents = [] #list of torrents to stop        
        # invalidTorrents = [] #invalid torrents
        # 
        # if (torsId != None): #check if the torId is defined, convert to integer
            # #check if it's not a list
            # if (not isinstance(torsId, list)):
                # torsId = [torsId]
            # 
            # for torId in torsId:
                # if (not re.search('^\d+?$', str(torId))): #check if is numeric
                    # invalidTorrents.append(str(torId))   
                    # break #stop the loop, invalid torrent id, go to the next one
                # 
                # #check if the id exist
                # found = False
                # for tor in self._transmission.torrents:
                    # if (tor.id == int(torId)):
                        # torrents.append(tor)
                        # found = True
                        # break #torrent found, stop the loop
                # 
                # if (not found): #if the torrent was not found, invalid
                    # invalidTorrents.append(str(torId))
                # #############    
                   # 
        # else: #get all torrents
            # torrents = self._transmission.torrents
        # 
        # #raise exception with the invalid torrents
        # if (len(invalidTorrents) > 0):
            # raise AttributeError("invalid torrents id '%s'" % (",".join(invalidTorrents)))
        # 
        # return torrents #return the list of the torrents


###command line object###
import cmd
import sys

class CmdManageTorrents(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        self.prompt = "torrents >> "
        self._transmission = ManageTorrents('localhost', 9091)
    
    def _print_torrents_header(self, columns):        
        #build lines with the headers
        lines = list()
        for col in columns:
            self._print_prepare_value_lines(col, lines, col['label'], columns)
        
        self._print_lines(lines, columns)
    
    def _print_torrents_separator(self, columns):
        output = ""
        for col in columns:
            output += '|'
            output += "".ljust(col['chars'], '-')
    
        return output + '|'
    
    def _print_lines(self, lines, columns):
        for line in lines:
            output = ""
            for col in columns:
                output += '|%s' % (line[col['name']])
            print(output + '|')
    
    def _print_prepare_value_lines(self, column, lines, value, columns):
        ##initial row data
        initialData = {}
        for col in columns:
            initialData[col['name']] = "".ljust(col['chars'])
        
        line = 0
        while (len(value) > 0):
            tmpValue = value[:column['chars']]
            
            lineData = initialData.copy()
            if (len(lines) <= line):
                lines.append(lineData)
            else:
                lineData = lines[line] 
                
            lineData[column['name']] = tmpValue.ljust(column['chars'])
            
            value = value[column['chars']:]
            line += 1
    
    def _print_torrents(self, torrents):
        columns = [{'name': 'id', 'label': 'ID', 'chars': 5},
                   {'name': 'name', 'label': 'Name', 'chars': 10},
                   {'name': 'size', 'label': 'Size', 'chars': 8},
                   {'name': 'downloaded_size', 'label': 'Dwn size', 'chars': 8},
                   {'name': 'progress', 'label': '%', 'chars': 4},
                   {'name': 'rateDownload', 'label': 'Dwn rate', 'chars': 4},
                   {'name': 'rateUpload', 'label': 'Upl rate', 'chars': 4},
                   {'name': 'status', 'label': 'Status', 'chars': 10},
                   {'name': 'eta', 'label': 'time to end', 'chars': 11}
                  ]
        
        #row separator
        rowSeparator = self._print_torrents_separator(columns)
        
        #print the columns header
        self._print_torrents_header(columns)
        print(rowSeparator)

        #get the data to print and print
        for torrent in torrents:
            lines = self._print_prepare_torrent_lines(columns, torrent)
            
            #print the lines
            self._print_lines(lines, columns)
                
            #print separator
            print(rowSeparator)
    
    def _print_prepare_torrent_lines(self, columns, torrent):
        ##initial row data
        initialData = {}
        for column in columns:
            initialData[column['name']] = "".ljust(column['chars'])
        
        lines = list()
        for column in columns:
            value = ""
            
            if (column['name'] in ['size', 'downloaded_size', 'rateDownload', 'rateUpload']):
                value = self._get_size(getattr(torrent, column['name']))
            elif (column['name'] == "progress"):
                value = str(round(torrent.progress, 2))
            elif (column['name'] == "eta"):
                value = torrent.format_eta()
            else:
                value = str(getattr(torrent, column['name']))
            
            #loop that controls, how many lines would be needed to the column
            self._print_prepare_value_lines(column, lines, value, columns)    
            
        return lines
    
    def _get_size(self, size):
        result = ""
        divisor = 8 #8 bits => 1 Byte
        suffix = "B" #Byte
        size = Decimal("%.15g" % size)
        
        if (size >= 1024 and size < 1048576): #check kilobyte
            suffix = "Kb"
            divisor = 1024
        elif (size >= 1048576 and size < 1073741824): #check megabyte
            suffix = "Mb"
            divisor = 1048576
        elif (size >= 1073741824): #check gigabyte (1073741824)
            suffix = "Gb"
            divisor = 1073741824
        
        result = str(round(Decimal(size) / divisor, 2)) + suffix
        
        return result
    
    def _get_time(self, time):
        result = ""
        suffix = "sec(s)"
        
        if (time > 60): #convert to minutes
            time = Decimal(time) / 60
            suffix = "min(s)"
            
            if (time > 60): #convert to hours
                time = time / 60
                suffix = "hour(s)"
                
                if (time > 24): #convert to days
                    time = time / 24
                    suffix = "day(s)"
        
        if (time > 0):
            time = int(time)
            result = "%s %s" % (time, suffix)
        
        return result
    
    #####shell command lines#####
    def do_list(self, arg=None):
        torrents = self._transmission.list(arg)
        self._print_torrents(torrents)
    
    def do_add(self, arg=None):
        if (arg != None and len(arg) > 0):
            self._transmission.add(arg.split())
        
    def do_remove(self, arg=None):
        if (arg != None and len(arg) > 0):
            self._transmission.remove(arg.split())
    
    def do_start(self, arg=None):
        if (arg != None and len(arg) > 0):
            self._transmission.start(arg.split())
    
    def do_stop(self, arg=None):
        if (arg != None and len(arg) > 0):
            self._transmission.stop(arg.split())
    
    def do_clear(self, arg=None):
        os.system('clear')
    
    def do_quit(self, arg=None):
        sys.exit(1)
        
    def do_exit(self, arg=None):
        self.do_quit()
        
    def emptyline(self):
        return None
        
    def default(self, arg=None):
        print('Invalid command: %s' % (arg)) 
        
if (__name__ == "__main__"):
    cmdLine = CmdManageTorrents()
    while (True):
        try:
            cmdLine.cmdloop()
        except Exception, (ex):
            print(ex.message)
